#lang racket


(require "list.rkt")

(provide :
         random-interval)


; range
;; s: step (integer/rational/real)
;; a: begining (open/closed)
;; b: end (open/closed)
;; n: count (positive integer)

(define (accumulate-ab-s! fn a b s (acc (list)))
  (if (fn a b)
      (accumulate-ab-s! fn (+ a s) b s (cons a acc))
      (reverse! acc)))

(define (accumulate-a-s-n! a s n (acc (list)))
  (if (<= n 0)
      acc
      (accumulate-a-s-n! a s (- n 1) (cons (+ a (* (- n 1) s)) acc))))


; range-ab-s [a..s..b]

(define (range-ab-s ap a bp b s)
  (match (list ap bp)
    ((list 'open 'open)
     (accumulate-ab-s! < (+ a s) b s))
    ((list 'open 'closed)
     (accumulate-ab-s! <= (+ a s) b s))
    ((list 'closed 'open)
     (accumulate-ab-s! < a b s))
    ((list 'closed 'closed)
     (accumulate-ab-s! <= a b s))))


; range-ab-n [a..[n]..b]

(define (range-ab-n ap a bp b n)
  (match (list ap bp)
    ((list 'open 'open)
     (let ((s (/ (- b a) (+ n 1))))
       (accumulate-a-s-n! (+ a s) s n)))
    ((list 'open 'closed)
     (let ((s (/ (- b a) n)))
       (accumulate-a-s-n! (+ a s) s n)))
    ((list 'closed 'open)
     (let ((s (/ (- b a) n)))
       (accumulate-a-s-n! a s n)))
    ((list 'closed 'closed)
     (let ((s (/ (- b a) (- n 1))))
       (accumulate-a-s-n! a s n)))))

(define (range-ab-n-list ap as bp bs ns)
  (apply cross (map (λ (a b n) (range-ab-n ap a bp b n)) as bs ns)))


; range-a-s-n  [a..s..[n]]

(define (range-a-s-n ap a s n)
  (match ap
    ('open
     (accumulate-a-s-n! (+ a s) s n))
    ('closed
     (accumulate-a-s-n! a s n))))


; range-ab [a..b] (s = 1)

(define (range-ab ap a bp b)
  (range-ab-s ap a bp b 1))


; range-an [a..[n]] (s = 1)

(define (range-a-n ap a n)
  (range-a-s-n ap a 1 n))


; range-abc-s [a,b..c] (s = b - a)

(define (range-abc-s ap a b cp c)
  (range-ab-s ap a cp c (- b a)))


; range-abc-n [a,b..[n]] (s = b - a)

(define (range-abc-n ap a b n)
  (range-a-s-n ap a (- b a) n))


; range [[n]] (a = 0, s = 1)

(define (range-n n)
  (range-a-n 'closed 0 n))


; range [a..[n]] (not number? a)

(define (range-repeat exp n)
  (repeat n (list exp)))


; range syntax

(define-syntax (: stx)
  (syntax-case stx (.. > <)
    ; range-ab-s
    ((_ > a .. s .. b <)
     #'(range-ab-s 'open a 'open b s))
    ((_ > a .. s .. b >)
     #'(range-ab-s 'open a 'closed b s))
    ((_ < a .. s .. b <)
     #'(range-ab-s 'closed a 'open b s))
    ((_ < a .. s .. b >)
     #'(range-ab-s 'closed a 'closed b s))
    
    ; range-ab-n
    ((_ > (a ...) .. < (n ...) > .. (b ...) <)
     #'(range-ab-n-list 'open (list a ...) 'open (list b ...) (list n ...)))
    ((_ > (a ...) .. < (n ...) > .. (b ...) >)
     #'(range-ab-n-list 'open (list a ...) 'closed (list b ...) (list n ...)))
    ((_ < (a ...) .. < (n ...) > .. (b ...) <)
     #'(range-ab-n-list 'closed (list a ...) 'open (list b ...) (list n ...)))
    ((_ < (a ...) .. < (n ...) > .. (b ...) >)
     #'(range-ab-n-list 'closed (list a ...) 'closed (list b ...) (list n ...)))
    
    ((_ > (a ...) .. < n > .. (b ...) <)
     #'(range-ab-n-list 'open (list a ...) 'open (list b ...) (list n n)))
    ((_ > (a ...) .. < n > .. (b ...) >)
     #'(range-ab-n-list 'open (list a ...) 'closed (list b ...) (list n n)))
    ((_ < (a ...) .. < n > .. (b ...) <)
     #'(range-ab-n-list 'closed (list a ...) 'open (list b ...) (list n n)))
    ((_ < (a ...) .. < n > .. (b ...) >)
     #'(range-ab-n-list 'closed (list a ...) 'closed (list b ...) (list n n)))
    
    ((_ > a .. < n > .. b <)
     #'(range-ab-n 'open a 'open b n))
    ((_ > a .. < n > .. b >)
     #'(range-ab-n 'open a 'closed b n))
    ((_ < a .. < n > .. b <)
     #'(range-ab-n 'closed a 'open b n))
    ((_ < a .. < n > .. b >)
     #'(range-ab-n 'closed a 'closed b n))
    
    ; range-a-s-n
    ((_ < a .. s .. < n > >)
     #'(range-a-s-n 'closed a s n))
    ((_ > a .. s .. < n > <)
     #'(range-a-s-n 'open a s n))

    ; range-ab
    ((_ > a .. b <)
     #'(range-ab 'open a 'open b))
    ((_ > a .. b >)
     #'(range-ab 'open a 'closed b))
    ((_ < a .. b <)
     #'(range-ab 'closed a 'open b))
    ((_ < a .. b >)
     #'(range-ab 'closed a 'closed b))
    
    ; range-a-n
    ((_ < a .. < n > >)
     #'(if (number? a)
           (range-a-n 'closed a n)
           (range-repeat a n)))
    ((_ > a .. < n > <)
     #'(range-a-n 'open a n))

    ; range-abc-s
    ((_ > a , b .. c <)
     #'(range-abc-s 'open a b 'open c))
    ((_ > a , b .. c >)
     #'(range-abc-s 'open a b 'closed c))
    ((_ < a , b .. c <)
     #'(range-abc-s 'closed a b 'open c))
    ((_ < a , b .. c >)
     #'(range-abc-s 'closed a b 'closed c))

    ; range-abc-n
    ((_ > a , b .. < n > <)
     #'(range-abc-n 'open a b 'open n))
    ((_ > a , b .. < n > >)
     #'(range-abc-n 'open a b 'closed n))
    ((_ < a , b .. < n > <)
     #'(range-abc-n 'closed a b 'open n))
    ((_ < a , b .. < n > >)
     #'(range-abc-n 'closed a b 'closed n))
    
    ; range-n
    ((_ n)
     #'(range-n n))))

(define (random-interval x0 x1)
  (+ x0 (* (- x1 x0) (random))))